import 'package:cunyutech_flutter_core/cunyutech_flutter_core.dart';
import 'package:cunyutech_flutter_ui/src/text/main.dart';
import 'package:cunyutech_flutter_ui/src/theme/main.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart' hide RefreshCallback;

import 'SliverListEmpty.dart';
import 'SliverListViewFooter.dart';

typedef Widget SliverListViewRenderItemBuilderDelegate(BuildContext context,dynamic item,int index);
typedef Future<void> EndReachedCallback();
typedef void EndReachedStateChange(EndReachedState state);
typedef Widget SliverListViewFooterBuilder(BuildContext context, EndReachedState state, {bool hasMore, TextStyle textStyle});

class SliverListViewState extends InheritedWidget {
  final EndReachedState reachedState;
  final BuildContext context;

  SliverListViewState({ this.context, this.reachedState, Key key, Widget child}): 
    super(key: key, child: child);

  @override
  bool updateShouldNotify(InheritedWidget oldWidget) {
    return true;
  }
}

class SliverListView extends StatefulWidget {
  /// 渲染列表项
  final SliverListViewRenderItemBuilderDelegate renderItem;
   /// 渲染下拉刷新器 可选
  final RefreshControlIndicatorBuilder renderRefresh;
  /// 列表数据
  final List<dynamic> data;
  /// 触发刷新事件 异步 可选
  final RefreshCallback onRefresh;
  /// 触发refresh零界点
  final double refreshIndicatorExtent;
  /// 触发refresh指示器的零界点
  final double refreshTriggerPullDistance;
  /// 触发上拉加载事件
  final EndReachedCallback onEndReached;
  // 触发endReached的零界点
  final double endReachedTriggerDistance;
  /// 列表尾部 可以渲染上拉加载组件
  final Widget footer;
  /// 头部
  final Widget header;
  /// 状态变更
  final EndReachedStateChange onEndReachedStateChange;
  /// 列表背景色，默认为透明色
  final Color backgroundColor;
  /// 内容为空时显示
  final Widget empty;
  /// 外界控制
  final ScrollController controller;
  final PageStorageKey<String> pageStorageKey;
  /// 是否反转
  final bool reverse;
  /// 是否展示SliverGrid
  final bool grid;
  /// 如果是grid必须要传这个参数
  final SliverGridDelegate gridDelegate;
  /// 滑动结束
  final VoidCallback onScrollEnd;
  /// 是否在加载过程中
  final bool firstLoading;
  /// 初次加载的骨架图
  final Widget firstLoadingSkeleton;

  final bool shrinkWrap;

  final String noNetworkIcon;

  static const double _defaultRefreshTriggerPullDistance = 100.0;
  static const double _defaultRefreshIndicatorExtent = 60.0;

  static SliverListViewState of(BuildContext context){
    return context.dependOnInheritedWidgetOfExactType<SliverListViewState>();
  }

  static defaultFooter(bool hasMore) {
    return SliverListViewFooter(
        hasMore: hasMore == true, 
        textStyle: TextStyle(fontSize: CYFontSize.Default),
    );
  }

  const SliverListView({ 
    Key key, 
    @required this.renderItem,
    @required this.data, 
    @required this.noNetworkIcon,
    this.onRefresh,
    this.renderRefresh = CupertinoSliverRefreshControl.buildSimpleRefreshIndicator,
    this.refreshIndicatorExtent = _defaultRefreshIndicatorExtent,
    this.refreshTriggerPullDistance = _defaultRefreshTriggerPullDistance,
    this.footer, 
    this.onEndReached, 
    this.endReachedTriggerDistance = _defaultEndReachedTriggerDistance,
    this.onEndReachedStateChange, 
    this.backgroundColor = Colors.transparent,
    this.header,
    this.empty,
    this.controller,
    this.pageStorageKey,
    this.reverse = false,
    this.shrinkWrap = false,
    this.grid = false,
    this.gridDelegate,
    this.onScrollEnd,
    this.firstLoading,
    this.firstLoadingSkeleton,
  }) : super(key: key);

  _SliverListViewState createState() => _SliverListViewState();
}

class _SliverListViewState extends State<SliverListView> {
  EndReachedState reachedState = EndReachedState.inactive;

  CYNetwork cyNetwork = CYNetwork();
  
  @override
  void initState() { 
    super.initState();
    cyNetwork.check((){
      setState(() {});
    });
  }

  @override
  void dispose() { 
    cyNetwork.dispose();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    if(widget.firstLoading == true) {
      return widget.firstLoadingSkeleton;
    }
    bool noData = widget.data == null || widget.data?.length == 0;

    List<Widget> slivers = [];
    if(widget.onRefresh !=null) {
      slivers.add(CupertinoSliverRefreshControl(
        builder: widget.renderRefresh,
        refreshIndicatorExtent: widget.refreshIndicatorExtent,
        refreshTriggerPullDistance: widget.refreshTriggerPullDistance,
        onRefresh: widget.onRefresh,
      ));
    }
    slivers.add(widget.header);

    if(cyNetwork.status != NetWorkStatus.Unknown) {
      if(widget.grid) {
        slivers.add(
          noData? widget.empty : 
          SliverSafeArea(
              top: false, // Top safe area is consumed by the navigation bar.
              sliver: SliverGrid(
                gridDelegate: widget.gridDelegate,
                delegate: SliverChildBuilderDelegate(
                  (BuildContext context, int index) => widget.renderItem(context, widget.data.elementAt(index), index), 
                  childCount: widget.data.length
                ),
              ),
            )
        );
      } else {
        slivers.add(
          noData ? widget.empty :
            SliverSafeArea(
              top: false, // Top safe area is consumed by the navigation bar.
              sliver: SliverList(
                delegate: SliverChildBuilderDelegate(
                  (BuildContext context, int index) => widget.renderItem(context, widget.data.elementAt(index), index), 
                  childCount: widget.data.length
                ),
              ),
            )
        );
      }
      if(!noData) {
        slivers.add(widget.footer);
      }
    } else {
      slivers.add(SliverListEmpty(
        onTap: widget.onRefresh,
        icon: Image.asset(widget.noNetworkIcon),
        child: CYDescrition("网络连接失败\n请检查您的手机联网状态", maxLines: null, alignment: Alignment.center, textAlign: TextAlign.center),
      ));
    }
    

    return SliverListViewState(
      reachedState: reachedState,
      context: context,
      child: CupertinoPageScaffold(
        backgroundColor: widget.backgroundColor,
        child: EndReachedNotificationListenerBuilder (
          onScrollEnd: widget.onScrollEnd,
          onEndReached: widget.onEndReached,
          onEndReachedStateChange: (_state) {
            if(widget.onEndReachedStateChange != null) widget.onEndReachedStateChange(_state);
            this.setState(() {
              reachedState = _state;
            });
          },
          child: CustomScrollView(
            key: widget.pageStorageKey,
            reverse: widget.reverse,
            shrinkWrap: widget.shrinkWrap,
            physics: BouncingScrollPhysics(),
            controller: widget.controller,
            slivers: slivers.where((w) => w !=null).toList(),
          ),
        )
      ),
    );
  }
}


const double _defaultEndReachedTriggerDistance = 10;

class EndReachedNotificationListenerBuilder extends StatefulWidget {
  final double endReachedTriggerDistance;
  final EndReachedCallback onEndReached;
  final Widget child;
  final EndReachedStateChange onEndReachedStateChange;
  final VoidCallback onScrollEnd;

  static EndReachedState of(BuildContext context) {
    final _EndReachedNotificationListenerBuilderState builderState = context.findRootAncestorStateOfType<_EndReachedNotificationListenerBuilderState>();
    return builderState.state;
  }

  EndReachedNotificationListenerBuilder({
    Key key, 
    this.endReachedTriggerDistance = _defaultEndReachedTriggerDistance, 
    this.onEndReached, 
    this.child,
    this.onEndReachedStateChange,
    this.onScrollEnd,
  }) : super(key: key);

  _EndReachedNotificationListenerBuilderState createState() => _EndReachedNotificationListenerBuilderState();
}

enum EndReachedState {
  /// 未激活
  inactive,
  /// 激活中
  active,
  // 正在加载更多
  reached, 
}


class _EndReachedNotificationListenerBuilderState extends State<EndReachedNotificationListenerBuilder> {
  EndReachedState state = EndReachedState.inactive;
  bool reachedLock = false;
  double activePixels;

  void dispatchNotification(EndReachedState newState, BuildContext context) {
    if(newState != state) {
      this.setState((){
        state = newState;
      });
      if(widget.onEndReachedStateChange != null) {
        widget.onEndReachedStateChange(newState);
      }
    }
  }

  EndReachedState transitionState(ScrollMetrics metrics, bool scrolling) {
    switch (state) {
      case EndReachedState.inactive:
        reachedLock = false;
        if(metrics.maxScrollExtent - widget.endReachedTriggerDistance<= metrics.pixels) {
          activePixels = metrics.pixels;
          return EndReachedState.active;
        }
        break;
      case EndReachedState.active:
        if(!scrolling) {
          return EndReachedState.reached;
        }
        break;
      case EndReachedState.reached:
        if(reachedLock) {
          break;
        }
        reachedLock = true;
        widget.onEndReached()..whenComplete((){
          dispatchNotification(EndReachedState.inactive, context);
        });
        break;
      default:
        break;
    }
    return state;
  }

  @override
  Widget build(BuildContext context) {
    if(widget.onEndReached == null) {
      return widget.child;
    }
    return NotificationListener<ScrollEndNotification>(
      child: NotificationListener<ScrollNotification>(
        onNotification: (ScrollNotification scrollNotification) {
          ScrollMetrics metrics = scrollNotification.metrics;
          bool over = metrics.maxScrollExtent<=metrics.pixels;
          if(over) {
            EndReachedState newState = transitionState(metrics, true);
            dispatchNotification(newState, context);
          }
          return false;
        },
        child: widget.child
      ),
      onNotification: (ScrollEndNotification scrollNotification) {
        ScrollMetrics metrics = scrollNotification.metrics;
        bool over = metrics.maxScrollExtent<=metrics.pixels;
        if(over) {
          EndReachedState newState = transitionState(metrics, false);
          dispatchNotification(newState, context);
        }
        if(widget.onScrollEnd != null) {
          widget.onScrollEnd();
        }
        return false;
      },
    );
  }
}

